package org.nebuloop.photo.organizer;

import java.io.File;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Properties;
import java.util.logging.Logger;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.nebuloop.photo.organizer.util.Config;
import org.nebuloop.photo.organizer.util.DateUtil;

import com.drew.lang.GeoLocation;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.exif.GpsDirectory;

public class DestDirCreator {
  private static final String DEFAULT_DATE_FORMAT = "yyyy/MM/yyyy-MM-dd";
  public final MessageFormat pathFormatter = new MessageFormat("{0}/{1}{2}{3}");
  public final SimpleDateFormat dateDirFormat;
  private boolean preserveNonNumericFolderName = true;
  private boolean useCameraInFolderName = false;
  
  public boolean isPreserveNonNumericFolderName() {
    return preserveNonNumericFolderName;
  }

  public void setPreserveNonNumericFolderName(boolean preserveNonNumericFolderName) {
    this.preserveNonNumericFolderName = preserveNonNumericFolderName;
  }

  public boolean isUseCameraInFolderName() {
    return useCameraInFolderName;
  }

  public void setUseCameraInFolderName(boolean useCameraInFolderName) {
    this.useCameraInFolderName = useCameraInFolderName;
  }

  private final String YEAR_TAG = "yyyy";
  private final String MONTH_TAG = "mm";
  private final String DATE_TAG = "dd";
  private final String CAMERA_MAKE_TAG = "camera.make";
  private final String CAMERA_MODEL_TAG = "camera.model";
  private final String NON_ALPHA_SRC_DIR_TAG = "misc.preserve.src.name";
  private final String GEO_COUNTRY_TAG = "geo.country";
  private final String GEO_COUNTY_TAG = "geo.county";
  private final String TAG_TAG = "tag";
  private final String TRIP_TAG = "geo.trip";
  
  private final boolean useYear;
  private final boolean useMonth;
  private final boolean useDate;
  private final boolean useCameraMake;
  private final boolean useCameraModel;
  private final boolean useNonAlphaSrcDirName;
  private final boolean useGeoCountry;
  private final boolean useGeoCounty;
  
  public DestDirCreator() {
    distDirPattern = Config.getString(Config.DEST_DIR_PATTERN, DEFAULT_DATE_FORMAT);
    useYear = hasTag(distDirPattern,YEAR_TAG);
    useMonth = hasTag(distDirPattern,MONTH_TAG);
    useDate = hasTag(distDirPattern,DATE_TAG);
    useCameraMake = hasTag(distDirPattern,CAMERA_MAKE_TAG);
    useCameraModel = hasTag(distDirPattern,CAMERA_MODEL_TAG);
    useNonAlphaSrcDirName = hasTag(distDirPattern,NON_ALPHA_SRC_DIR_TAG);
    useGeoCountry = hasTag(distDirPattern,GEO_COUNTRY_TAG);
    useGeoCounty = hasTag(distDirPattern,GEO_COUNTY_TAG);
    
    String dateFormat = distDirPattern.replaceAll("mm", "MM");
    Logger.getLogger(DestDirCreator.class.getName()).info("Destination directory date format : " + dateFormat);
    //dateDirFormat = new SimpleDateFormat(dateFormat);
    dateDirFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT);
  }

  private boolean hasTag(String format, String tag) {
    return format.indexOf("${" + tag + "}") >= 0;
  }

  public String getImageOutputPath(Metadata metadata, Date imageCreateTime, File srcImageFile,
      String destinationDirectory, String cameraMake, String cameraModel) {
    String srcParentName = srcImageFile.getParentFile().getName();
    boolean appendPathName = false;
    char[] chars = srcParentName.toCharArray();
    int i = 0;
    boolean isDate = DateUtil.isDate(srcParentName);
    
    if (!isDate && preserveNonNumericFolderName) {
      for (i = 0; i < chars.length; i++) {
        char c = chars[i];
        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
          appendPathName = true;
          break;
        }
      }
    }
    
    

    String dirNameSiffix = appendPathName ? " - " + srcParentName.substring(i).trim() : "";
    String cameraNameSuffix;
    if (useCameraInFolderName) {
      StringBuilder cameraInfo = new StringBuilder();
      boolean isMakePresent = StringUtils.isNotBlank(cameraMake);
      if (isMakePresent) {
        cameraInfo.append(cameraMake);
      }
      if (StringUtils.isNotBlank(cameraModel)) {
        if (isMakePresent) {
          if (cameraModel.contains(cameraMake)) {
            // camera make is in the model also
            // hence remove the duplicate
            cameraInfo.setLength(0);
          } else {
            cameraInfo.append('_');
          }
        }
        cameraInfo.append(cameraModel);
      }
      if (cameraInfo.length() > 0) cameraInfo.insert(0, '_');
      cameraNameSuffix = cameraInfo.toString().replace(' ','_').trim();
    } else {
      cameraNameSuffix = "";
    }

    String strDestDir = pathFormatter.format(new Object[] { destinationDirectory, 
        dateDirFormat.format(imageCreateTime), dirNameSiffix, 
        cameraNameSuffix });

    return strDestDir;
  }
  
  public String getImageOutputPath(File srcImageFile,
      String destinationDirectory, Metadata metadata, Date imageCreateTime) {
    String srcParentName = srcImageFile.getParentFile().getName();
    boolean appendPathName = false;
    char[] chars = srcParentName.toCharArray();
    int i = 0;
    boolean isDate = DateUtil.isDate(srcParentName);
    
    if (!isDate && useNonAlphaSrcDirName) {
      for (i = 0; i < chars.length; i++) {
        char c = chars[i];
        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
          appendPathName = true;
          break;
        }
      }
    }
    
    Properties props = new Properties();
    Calendar cal = Calendar.getInstance();
    cal.setTime(imageCreateTime);
    if (useYear) props.setProperty(YEAR_TAG, String.valueOf(cal.get(Calendar.YEAR)));
    if (useMonth) props.setProperty(MONTH_TAG, String.valueOf(cal.get(Calendar.MONTH)));
    if (useDate) props.setProperty(DATE_TAG, String.valueOf(cal.get(Calendar.DAY_OF_MONTH)));

    ExifIFD0Directory exifIFD0Dir = metadata.getDirectory(ExifIFD0Directory.class);
    GpsDirectory gpsDir = metadata.getDirectory(GpsDirectory.class);

    if (exifIFD0Dir != null) {
      String cameraMake = exifIFD0Dir.getString(ExifIFD0Directory.TAG_MAKE);
      String cameraModel = exifIFD0Dir.getString(ExifIFD0Directory.TAG_MODEL);
      boolean isCameraMake = useCameraMake && StringUtils.isNotBlank(cameraMake);
      if (isCameraMake) {
        props.setProperty(CAMERA_MAKE_TAG, cameraMake);
      }
      if (useCameraModel && StringUtils.isNotBlank(cameraModel)) {
        props.setProperty(CAMERA_MODEL_TAG, cameraModel);
        if (isCameraMake && cameraModel.indexOf(cameraMake) > -1) {
          props.setProperty(CAMERA_MAKE_TAG, "");
        }
      }
      
    }
    boolean hasGeocodes =  (gpsDir != null && gpsDir.getGeoLocation() != null);
    if (hasGeocodes) {
      GeoLocation geoLoc = gpsDir.getGeoLocation();
    }    
    

    if (appendPathName) {
      props.setProperty(NON_ALPHA_SRC_DIR_TAG, srcParentName.substring(i).trim());
    }

    StrSubstitutor sub = new StrSubstitutor(props);
    
    String destPathName = sub.replace(distDirPattern);

    StringBuilder sb = new StringBuilder();
    boolean isPrevDelim = false;
    for (char c:destPathName.toCharArray()) {
      boolean isDelim = c=='_' || c=='-' || c=='/' || c=='\\';
      if (isPrevDelim && isDelim) {
        continue;
      }
      isPrevDelim=isDelim;
      sb.append(c);
    }
    return sb.toString();
  }
  
  private static volatile DestDirCreator instance;
  private String distDirPattern;
  public static DestDirCreator getInstance() {
    if (instance == null) {
      synchronized (DestDirCreator.class) {
        if (instance ==null) {
          instance = new DestDirCreator();
        }
      }
    }
    return instance;
  }
}
